package hk.edu.polyu.comp.motioncapture.application;

import hk.edu.polyu.comp.motioncapture.point.Frame;
import hk.edu.polyu.comp.motioncapture.point.FrameSet;
import hk.edu.polyu.comp.motioncapture.point.OptiPoint;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

import com.jacob.activeX.*;
import com.jacob.com.*;

public class OptiTrackServer {

	protected Shell shell;

	protected Canvas canvas;

	protected ActiveXComponent objNPCameraCollection = null;
	protected FrameSet frameSet;

	// protected int currentFrame = -1;

	/**
	 * Launch the application
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			OptiTrackServer window = new OptiTrackServer();
			window.open();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Open the window
	 */
	public void open() {
		final Display display = Display.getDefault();
		createContents();
		init();
		shell.open();
		shell.layout();
		while (!shell.isDisposed()) {
			if (!display.readAndDispatch())
				display.sleep();
		}
	}

	/**
	 * Create contents of the window
	 */
	protected void createContents() {
		shell = new Shell();
		shell.addDisposeListener(new DisposeListener() {
			public void widgetDisposed(final DisposeEvent arg0) {
				if (objNPCameraCollection != null) {
					int cameraCount = objNPCameraCollection
							.getPropertyAsInt("Count");

					Variant vi = new Variant();
					for (int i = 0; i < cameraCount; i++) {
						vi.putInt(i);
						Dispatch camera = objNPCameraCollection.invoke("Item",
								vi).getDispatch();

						Dispatch.call(camera, "Stop");

						Dispatch.call(camera, "Close");
					}
				}

				ComThread.Release();
				System.out.println("End");
			}
		});
		shell.setMinimumSize(new Point(320, 280));
		final GridLayout gridLayout = new GridLayout();
		gridLayout.marginWidth = 0;
		gridLayout.marginHeight = 0;
		shell.setLayout(gridLayout);
		shell.setSize(480, 400);
		shell.setText("OptiTrack");

		canvas = new Canvas(shell, SWT.NONE);
		canvas.addPaintListener(new PaintListener() {
			public void paintControl(final PaintEvent arg0) {
				GC gc = arg0.gc;

				Color blackColor = new Color(shell.getDisplay(), new RGB(0, 0,
						0));
				Color greenColor = new Color(shell.getDisplay(), new RGB(0,
						255, 0));

				gc.setBackground(blackColor);

				gc.fillRectangle(0, 0, canvas.getBounds().width, canvas
						.getBounds().height);

				gc.setBackground(greenColor);
				Frame frame = frameSet.pop();
				if (frame != null) {
					// System.out.println("Frame count:" + frame.size());
					for (hk.edu.polyu.comp.motioncapture.point.Point pt : frame
							.getPointArray()) {
						OptiPoint point = (OptiPoint) pt;

						gc.fillOval((int) (point.getX() - point.getArea() / 2),
								(int) (point.getY() - point.getArea() / 2),
								(int) point.getArea(), (int) point.getArea());
					}
				} else {
					System.out.println("Frame set is empty");
				}

				gc.dispose();
			}
		});
		canvas.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
		//
	}

	protected void init() {
		System.out.println("Start");
		ComThread.InitSTA();

		frameSet = new FrameSet();

		objNPCameraCollection = new ActiveXComponent(
				"OptiTrack.NPCameraCollection");

		DispatchEvents camerasEvent = new DispatchEvents(objNPCameraCollection,
				this);
		if (camerasEvent == null) {
			System.out.println("ERROR in creating event listener");
			return;
		}

		objNPCameraCollection.invoke("Enum");

		int cameraCount = objNPCameraCollection.getPropertyAsInt("Count");
		System.out.println("Camera count: " + cameraCount);
		System.out.println();

		Variant vi = new Variant();
		for (int i = 0; i < cameraCount; i++) {
			vi.putInt(i);
			Dispatch camera = objNPCameraCollection.invoke("Item", vi)
					.getDispatch();

			Dispatch.call(camera, "Open");

			int serialNumber = Dispatch.get(camera, "SerialNumber").getInt();
			System.out.format("Camera %d - Serial number: %d\n", i,
					serialNumber);

			int model = Dispatch.get(camera, "Model").getInt();
			System.out.format("Camera %d - Model: %s\n", i, Integer
					.toHexString(model).toUpperCase());

			int revision = Dispatch.get(camera, "Revision").getInt();
			System.out.format("Camera %d - Revision: %s\n", i, Integer
					.toHexString(revision).toUpperCase());

			int width = Dispatch.get(camera, "Width").getInt();
			System.out.format("Camera %d - Width: %d\n", i, width);

			int height = Dispatch.get(camera, "Height").getInt();
			System.out.format("Camera %d - Height: %d\n", i, height);

			int frameRate = Dispatch.get(camera, "FrameRate").getInt();
			System.out.format("Camera %d - FrameRate: %d\n", i, frameRate);

			DispatchEvents cameraEvent = new DispatchEvents(camera, this);
			if (cameraEvent == null) {
				System.out.println("ERROR in creating event listener");
				break;
			}

			Dispatch.call(camera, "Start");
		}
	}

	public void DeviceArrival(Variant[] args) {
		System.out.println("DeviceArrival");
	}

	public void DeviceRemoval(Variant[] args) {
		System.out.println("DeviceRemoval");
	}

	public void FrameAvailable(Variant[] args) {
		// System.out.println("FrameAvailable");

		ActiveXComponent camera = new ActiveXComponent(args[0].getDispatch());

		ActiveXComponent frame = new ActiveXComponent(camera.invoke("GetFrame",
				new Variant(0)).getDispatch());
		int pointCount = frame.getPropertyAsInt("Count");
		// System.out.println("Point count: " + pointCount);

		Variant vi = new Variant();
		for (int i = 0; i < pointCount; i++) {
			vi.putInt(i);
			Dispatch point = frame.invoke("Item", vi).getDispatch();

			double x = Dispatch.get(point, "X").getDouble();
			double y = Dispatch.get(point, "Y").getDouble();
			int w = Dispatch.get(point, "Width").getInt();
			int h = Dispatch.get(point, "Height").getInt();
			double a = Dispatch.get(point, "Area").getDouble();

			if (frameSet != null) {
				Frame f = new Frame();
				OptiPoint pt = new OptiPoint(0, x, y, w, h, a, 0);

				f.add(pt);

				frameSet.push(f);
			}
			// System.out.format("Point: %3.2f, %3.2f\n", x, y);

			shell.getDisplay().syncExec(new Runnable() {
				public void run() {
					canvas.redraw();
				}
			});
		}

		frame.invoke("Free");
	}

	public void SwitchChange(Variant[] args) {
		System.out.println("SwitchChange");
	}
}
